﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.IO;
using System.Text.RegularExpressions;
using CodeIn.Diagnostics;
using System.Windows.Media.Imaging;
using Mvvm;
using System.Windows.Input;
using CodeIn.Core;
using System.Windows.Data;

namespace CodeIn.Modules.Ui.FileExplorer.ContextMenus {
	public class ContextMenus {
		/// <summary>
		/// Gets the current instance containing every menus.
		/// </summary>
		public static ContextMenus Instance { get { return instance; } }

		/// <summary>
		/// Context menus for drive items
		/// </summary>
		public List<ContextMenuTuple> DriveMenus { get; private set; }

		/// <summary>
		/// Context menus for directory items
		/// </summary>
		public List<ContextMenuTuple> DirectoryMenus { get; private set; }

		/// <summary>
		/// Context menus for file items
		/// </summary>
		public List<ContextMenuTuple> FileMenus { get; private set; }

		private static ContextMenus instance = new ContextMenus();

		private ContextMenus() {
			DriveMenus = new List<ContextMenuTuple>();
			DirectoryMenus = new List<ContextMenuTuple>();
			FileMenus = new List<ContextMenuTuple>();

			string uri = "pack://application:,,,/resources/modules/config/ui.fileexplorer/ContextMenus.txt";
			string content = GetContent(uri);

			if (content != null)
				ParseContent(content);
			else
				MessageHandlers.Debug.WriteErrorLine(
					"FileExplorer ContextMenus resource file cannot be found:\n" + uri);
		}

		/// <summary>
		/// Returns the content of the resource file as a string
		/// </summary>
		private string GetContent(string resourcePath) {
			var info = App.GetResourceStream(new Uri(resourcePath));
			if (info == null)
				return null;

			Stream menuStream = info.Stream;
			StreamReader sr = new StreamReader(menuStream);
			return sr.ReadToEnd();
		}

		/// <summary>
		/// Parses content and populates context menu lists
		/// </summary>
		private void ParseContent(string content) {
			// Remove comment lines
			content = Regex.Replace(content, @"//.*$", "", RegexOptions.Multiline);

			// Parse groups
			var matches = Regex.Matches(content, @"^ \s* (FILE|DIRECTORY|DRIVE) \s* : \s* ""(.*?)"" \s* { (.*?) }",
				RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Multiline);

			// For each found groups...
			foreach (Match m in matches) {
				// Create menu and retrieve fields
				ContextMenuTuple menuTuple = new ContextMenuTuple();
				string type = m.Groups[1].Value;
				string filter = m.Groups[2].Value;
				string actions = m.Groups[3].Value;

				// Convert filter to a regex and assign it to the context menu tuple
				filter = Regex.Escape(filter);
				filter = filter.Replace("\\*", ".*?");
				filter = "^" + filter + "$";
				menuTuple.Filter = filter;

				// Retrieve action lines
				string[] actionLines = actions.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

				// For each action line...
				foreach (var line in actionLines) {
					ContextMenuAction action = ParseActionLine(line);
					if (action != null) {
						if (action.Type == ContextMenuAction.ActionTypes.SEPARATOR) {
							menuTuple.Menu.Items.Add(new Separator());
						} else {
							MenuItem item = new MenuItem();
							item.Header = action.ItemName;
							item.Icon = action.Icon;
							item.CommandParameter = action;
							item.SetBinding(MenuItem.CommandProperty, "LaunchContextMenuActionCommand");
							menuTuple.Menu.Items.Add(item);
						}
					}
				}

				// Add the menu tuple to the right list
				switch (type) {
					case "DRIVE": DriveMenus.Add(menuTuple); break;
					case "DIRECTORY": DirectoryMenus.Add(menuTuple); break;
					case "FILE": FileMenus.Add(menuTuple); break;
				}
			}
		}

		/// <summary>
		/// Parses an action line and returns its content as a ContextMenuAction
		/// </summary>
		private ContextMenuAction ParseActionLine(string line) {
			// Discard empty lines
			if (string.IsNullOrWhiteSpace(line))
				return null;

			// Split the line
			string[] args = line.Split(',');

			// If the line is badly written, ignore it
			if (args.Length != 4) {
				MessageHandlers.Debug.WriteWarningLine(
					"FileExplorer context builder, unrecognized line:\n" + line);
				return null;
			}

			// Retrieve elements
			string itemName = args[0].Trim();
			string iconName = args[1].Trim();
			string actionType = args[2].Trim();
			string action = Regex.Match(args[3].Trim(), @"""(.*?)""$").Groups[1].Value;

			// Build action
			ContextMenuAction ret = new ContextMenuAction();
			ret.ItemName = itemName;
			ret.Icon = GetIcon(iconName);
			ret.Type = (ContextMenuAction.ActionTypes)Enum.Parse(typeof(ContextMenuAction.ActionTypes), actionType);
			ret.Content = action;

			return ret;
		}

		/// <summary>
		/// Retrieve a bitmap image from the name of an icon
		/// </summary>
		private BitmapImage GetIcon(string iconName) {
			if (string.IsNullOrWhiteSpace(iconName))
				return null;

			string uri = "pack://application:,,,/resources/gfx/" + iconName;
			return new BitmapImage(new Uri(uri));
		}
	}
}
